package com.example.service.impl;

import com.example.dto.AnnouncementRequest;
import com.example.dto.ApiResponse;
import com.example.entity.Announcement;
import com.example.entity.AnnouncementFile;
import com.example.mapper.AnnouncementMapper;
import com.example.mapper.AnnouncementFileMapper;
import com.example.service.AnnouncementService;
import com.example.service.UserService;
import com.example.utils.LogUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.PostConstruct;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class AnnouncementServiceImpl implements AnnouncementService {

    private static final Logger logger = LoggerFactory.getLogger(AnnouncementServiceImpl.class);
    private static final Logger businessLogger = LoggerFactory.getLogger("businessLogger");

    private static final Set<String> ALLOWED_FILE_TYPES = new HashSet<>(Arrays.asList(
        "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx",
        "txt", "jpg", "jpeg", "png", "gif"
    ));
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB

    @Value("${file.upload.path}")
    private String uploadPath;

    @Autowired
    private AnnouncementMapper announcementMapper;

    @Autowired
    private AnnouncementFileMapper fileMapper;

    @Autowired
    private UserService userService;

    @PostConstruct
    public void init() {
        try {
            Path uploadDir = Paths.get(uploadPath);
            if (!Files.exists(uploadDir)) {
                Files.createDirectories(uploadDir);
            }
            logger.info("文件上传目录已创建: {}", uploadDir.toAbsolutePath());
        } catch (Exception e) {
            logger.error("创建文件上传目录失败", e);
            throw new RuntimeException("无法创建文件上传目录", e);
        }
    }

    @Override
    public List<Announcement> getAllPublishedAnnouncements() {
        return announcementMapper.findAllPublished();
    }
    @Override
    public Map<String, Object> getPublishedAnnouncementsWithPage(int page, int size) {
        // 参数验证
        if (page < 1) page = 1;
        if (size < 1) size = 10;
        if (size > 100) size = 100;  // 限制最大页面大小
        
        // 计算偏移量
        int offset = (page - 1) * size;
        
        // 获取总记录数
        long total = announcementMapper.countPublished();
        
        // 获取当前页数据
        List<Announcement> announcements = announcementMapper.findPublishedWithPage(offset, size);
        
        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("page", page);
        result.put("size", size);
        result.put("announcements", announcements);
        
        return result;
    }

    @Override
    public Map<String, Object> getAllAnnouncementsWithPage(int page, int size, Integer status) {
        // 参数验证
        if (page < 1) page = 1;
        if (size < 1) size = 10;
        if (size > 100) size = 100;

        // 计算偏移量
        int offset = (page - 1) * size;

        // 获取总记录数
        long total = announcementMapper.countAll(status);

        // 获取当前页数据
        List<Announcement> announcements =
                announcementMapper.findAllWithPage(offset, size, status);

        // 构建返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("page", page);
        result.put("size", size);
        result.put("announcements", announcements);

        return result;
    }



    @Override
    public List<Announcement> getAllAnnouncements() {
        return announcementMapper.findAllOrderByCreateTime();
    }

    @Override
    public Announcement getAnnouncementById(Long id) {
        return announcementMapper.selectById(id);
    }

    @Override
    @Transactional
    public ApiResponse<?> createAnnouncement(AnnouncementRequest request) {
        Long currentUserId = userService.getCurrentUserId();
        if (currentUserId == null) {
            return ApiResponse.error("User not authenticated");
        }

        Announcement announcement = new Announcement();
        announcement.setTitle(request.getTitle());
        announcement.setContent(request.getContent());
        announcement.setCreatedBy(currentUserId);
        announcement.setLastModifiedBy(currentUserId);
        announcement.setCreateTime(LocalDateTime.now());
        announcement.setUpdateTime(LocalDateTime.now());
        announcement.setIsPublished(request.getIsPublished());

        if (Boolean.TRUE.equals(request.getIsPublished())) {
            announcement.setPublishTime(LocalDateTime.now());
        }

        announcementMapper.insert(announcement);

        // 记录业务日志
        LogUtils.logBusinessOperation(
            businessLogger,
            "ANNOUNCEMENT_CREATE",
            "User " + currentUserId + " created announcement with ID " + announcement.getId(),
            currentUserId
        );

        return ApiResponse.success("Announcement created successfully", announcement);
    }

    @Override
    @Transactional
    public ApiResponse<?> updateAnnouncement(Long id, AnnouncementRequest request) {
        Long currentUserId = userService.getCurrentUserId();
        if (currentUserId == null) {
            return ApiResponse.error("User not authenticated");
        }

        Announcement announcement = announcementMapper.selectById(id);
        if (announcement == null) {
            return ApiResponse.error("Announcement not found");
        }

        announcement.setTitle(request.getTitle());
        announcement.setContent(request.getContent());
        announcement.setLastModifiedBy(currentUserId);
        announcement.setUpdateTime(LocalDateTime.now());

        // 如果发布状态改变
        if (request.getIsPublished() != null && !request.getIsPublished().equals(announcement.getIsPublished())) {
            announcement.setIsPublished(request.getIsPublished());
            if (Boolean.TRUE.equals(request.getIsPublished())) {
                announcement.setPublishTime(LocalDateTime.now());
            }
        }

        announcementMapper.updateById(announcement);

        // 记录业务日志
        LogUtils.logBusinessOperation(
            businessLogger,
            "ANNOUNCEMENT_UPDATE",
            "User " + currentUserId + " updated announcement with ID " + id,
            currentUserId
        );

        return ApiResponse.success("Announcement updated successfully", announcement);
    }

    @Override
    @Transactional
    public ApiResponse<?> deleteAnnouncement(Long id) {
        try {
            Long currentUserId = userService.getCurrentUserId();
            if (currentUserId == null) {
                return ApiResponse.error("用户未认证");
            }

            // 1. 获取公告信息
            Announcement announcement = announcementMapper.selectById(id);
            if (announcement == null) {
                return ApiResponse.error("公告不存在");
            }

            // 2. 获取公告相关的所有文件记录
            List<AnnouncementFile> files = fileMapper.findByAnnouncementId(id);

            // 3. 删除物理文件
            if (!files.isEmpty()) {
                Path announcementDir = Paths.get(uploadPath, "announcements", String.valueOf(id));
                try {
                    // 删除目录及其内容
                    if (Files.exists(announcementDir)) {
                        Files.walk(announcementDir)
                            .sorted((p1, p2) -> -p1.compareTo(p2)) // 反向排序，确保先删除文件再删除目录
                            .forEach(path -> {
                                try {
                                    Files.delete(path);
                                    logger.info("已删除文件或目录: {}", path);
                                } catch (IOException e) {
                                    logger.error("删除文件或目录失败: {}", path, e);
                                }
                            });
                        logger.info("已删除公告附件目录: {}", announcementDir);
                    }
                } catch (IOException e) {
                    logger.error("删除公告附件目录失败: {}", announcementDir, e);
                    return ApiResponse.error("删除公告附件失败: " + e.getMessage());
                }
            }

            // 4. 删除数据库中的文件记录
            if (!files.isEmpty()) {
                fileMapper.deleteByAnnouncementId(id);
                logger.info("已删除公告相关的文件记录，公告ID: {}", id);
            }

            // 5. 删除公告记录
            announcementMapper.deleteById(id);

            // 6. 记录业务日志
            LogUtils.logBusinessOperation(
                businessLogger,
                "ANNOUNCEMENT_DELETE",
                "用户 " + currentUserId + " 删除了公告 " + id,
                currentUserId
            );

            return ApiResponse.success("公告删除成功", null);
        } catch (Exception e) {
            logger.error("删除公告失败", e);
            throw new RuntimeException("删除公告失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional
    public ApiResponse<?> togglePublishStatus(Long id, Boolean isPublished) {
        Long currentUserId = userService.getCurrentUserId();
        if (currentUserId == null) {
            return ApiResponse.error("User not authenticated");
        }

        Announcement announcement = announcementMapper.selectById(id);
        if (announcement == null) {
            return ApiResponse.error("Announcement not found");
        }

        announcement.setIsPublished(isPublished);
        announcement.setLastModifiedBy(currentUserId);
        announcement.setUpdateTime(LocalDateTime.now());

        if (Boolean.TRUE.equals(isPublished)) {
            announcement.setPublishTime(LocalDateTime.now());
        }

        announcementMapper.updateById(announcement);

        // 记录业务日志
        String action = isPublished ? "published" : "unpublished";
        LogUtils.logBusinessOperation(
            businessLogger,
            "ANNOUNCEMENT_" + (isPublished ? "PUBLISH" : "UNPUBLISH"),
            "User " + currentUserId + " " + action + " announcement with ID " + id,
            currentUserId
        );

        return ApiResponse.success("Announcement " + action + " successfully", announcement);
    }

    @Override
    @Transactional
    public ApiResponse<?> createAnnouncementWithFiles(AnnouncementRequest request, List<MultipartFile> files) {
        try {
            // 创建公告
            Announcement announcement = new Announcement();
            announcement.setTitle(request.getTitle());
            announcement.setContent(request.getContent());
            announcement.setCreatedBy(userService.getCurrentUserId());
            announcement.setLastModifiedBy(userService.getCurrentUserId());
            announcement.setCreateTime(LocalDateTime.now());
            announcement.setUpdateTime(LocalDateTime.now());
            announcement.setIsPublished(request.getIsPublished());
            
            if (Boolean.TRUE.equals(request.getIsPublished())) {
                announcement.setPublishTime(LocalDateTime.now());
            }
            
            // 保存公告
            announcementMapper.insert(announcement);
            
            // 处理文件上传
            List<AnnouncementFile> uploadedFiles = new ArrayList<>();
            if (files != null && !files.isEmpty()) {
                for (MultipartFile file : files) {
                    try {
                        AnnouncementFile announcementFile = handleFileUpload(file, announcement.getId());
                        if (announcementFile != null) {
                            uploadedFiles.add(announcementFile);
                        }
                    } catch (Exception e) {
                        logger.error("处理文件失败: {}", file.getOriginalFilename(), e);
                    }
                }
            }
            
            Map<String, Object> result = new HashMap<>();
            result.put("announcement", announcement);
            result.put("files", uploadedFiles);
            
            return ApiResponse.success("公告创建成功", result);
        } catch (Exception e) {
            logger.error("创建公告失败", e);
            throw new RuntimeException("创建公告失败: " + e.getMessage());
        }
    }
    
    @Override
    public ApiResponse<?> getAnnouncementWithFiles(Long id) {
        // 1. 获取公告基本信息
        Announcement announcement = announcementMapper.selectById(id);
        if (announcement == null) {
            return ApiResponse.error("公告不存在");
        }
        
        // 2. 获取附件信息
        List<AnnouncementFile> files = fileMapper.findByAnnouncementId(id);
        
        // 3. 组装返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("announcement", announcement);
        result.put("files", files);
        
        return ApiResponse.success("获取公告成功", result);
    }
    
    @Override
    public ResponseEntity<Resource> downloadFile(Long fileId) {
        try {
            AnnouncementFile file = fileMapper.selectById(fileId);
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            
            // 获取URL
            String fileUrl = file.getUrl();
            logger.info("原始文件URL: {}", fileUrl);
            
            // 当前运行目录
            Path currentPath = Paths.get("").toAbsolutePath();
            logger.info("当前运行目录: {}", currentPath);
            
            // 配置的上传路径
            logger.info("配置的上传路径: {}", uploadPath);
            
            // 解析文件路径 - 考虑两种格式
            Path filePath;
            
            if (fileUrl.startsWith("/uploads/announcements/")) {
                // 格式: /uploads/announcements/{id}/{filename}
                String relativePath = fileUrl.substring(1); // 移除前导斜杠
                filePath = Paths.get(uploadPath).getParent().resolve(relativePath);
            } else if (fileUrl.startsWith("/announcements/")) {
                // 格式: /announcements/{id}/{filename}
                String[] parts = fileUrl.split("/");
                if (parts.length >= 4) {
                    String announcementId = parts[2];
                    String filename = parts[3];
                    filePath = Paths.get(uploadPath, "announcements", announcementId, filename);
                } else {
                    throw new RuntimeException("文件URL格式不正确: " + fileUrl);
                }
            } else {
                // 尝试直接使用URL
                filePath = Paths.get(uploadPath).resolve(fileUrl.substring(1));
            }
            
            logger.info("尝试访问文件路径: {}", filePath.toAbsolutePath().normalize());
            
            // 尝试替代路径 - 如果上面的路径不存在
            Path alternativePath = Paths.get(".", "uploads").resolve(fileUrl.substring(fileUrl.indexOf("/announcements/") + 1));
            logger.info("备用文件路径: {}", alternativePath.toAbsolutePath().normalize());
            
            // 检查路径是否存在
            Resource resource;
            try {
                resource = new UrlResource(filePath.toUri());
                if (!resource.exists() || !resource.isReadable()) {
                    logger.warn("主路径不可访问，尝试备用路径");
                    resource = new UrlResource(alternativePath.toUri());
                }
            } catch (Exception e) {
                logger.warn("主路径异常，尝试备用路径", e);
                resource = new UrlResource(alternativePath.toUri());
            }

            if (!resource.exists()) {
                logger.error("文件不存在");
                throw new RuntimeException("文件不存在");
            }
            
            if (!resource.isReadable()) {
                logger.error("文件不可读");
                throw new RuntimeException("文件不可读");
            }

            logger.info("成功找到文件: {}", resource.getFilename());
            
            return ResponseEntity.ok()
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .header(HttpHeaders.CONTENT_DISPOSITION,
                    "attachment; filename=\"" + URLEncoder.encode(file.getFilename(), StandardCharsets.UTF_8) + "\"")
                .body(resource);
        } catch (Exception e) {
            logger.error("文件下载失败", e);
            throw new RuntimeException("文件下载失败: " + e.getMessage());
        }
    }
    
    @Override
    public String getFileUrl(Long fileId) {
        AnnouncementFile file = fileMapper.selectById(fileId);
        if (file == null) {
            throw new RuntimeException("文件不存在");
        }
        return file.getUrl();
    }
    
    @Override
    public ResponseEntity<Resource> viewFile(Long fileId) {
        try {
            AnnouncementFile file = fileMapper.selectById(fileId);
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }

            Path filePath = Paths.get(uploadPath).resolve(file.getUrl().substring(1));
            Resource resource = new UrlResource(filePath.toUri());

            if (!resource.exists() || !resource.isReadable()) {
                throw new RuntimeException("文件不可读或不存在");
            }
            
            // 根据文件类型设置适当的Content-Type
            MediaType mediaType = determineMediaType(file.getType());
            
            return ResponseEntity.ok()
                .contentType(mediaType)
                .body(resource);
        } catch (Exception e) {
            logger.error("查看文件失败", e);
            throw new RuntimeException("查看文件失败: " + e.getMessage());
        }
    }
    
    private MediaType determineMediaType(String fileType) {
        if (fileType == null) {
            return MediaType.APPLICATION_OCTET_STREAM;
        }
        
        switch (fileType.toLowerCase()) {
            case "pdf":
                return MediaType.APPLICATION_PDF;
            case "png":
                return MediaType.IMAGE_PNG;
            case "jpg":
            case "jpeg":
                return MediaType.IMAGE_JPEG;
            case "gif":
                return MediaType.IMAGE_GIF;
            case "txt":
                return MediaType.TEXT_PLAIN;
            case "html":
                return MediaType.TEXT_HTML;
            case "doc":
            case "docx":
                return MediaType.parseMediaType("application/msword");
            case "xls":
            case "xlsx":
                return MediaType.parseMediaType("application/vnd.ms-excel");
            case "ppt":
            case "pptx":
                return MediaType.parseMediaType("application/vnd.ms-powerpoint");
            default:
                return MediaType.APPLICATION_OCTET_STREAM;
        }
    }
    
    private AnnouncementFile handleFileUpload(MultipartFile file, Long announcementId) {
        try {
            // 文件验证
            if (file.isEmpty()) {
                throw new IllegalArgumentException("文件为空");
            }

            if (file.getSize() > MAX_FILE_SIZE) {
                throw new IllegalArgumentException("文件大小超过限制（最大10MB）");
            }

            String originalFilename = file.getOriginalFilename();
            if (originalFilename == null || originalFilename.trim().isEmpty()) {
                throw new IllegalArgumentException("文件名为空");
            }

            String extension = FilenameUtils.getExtension(originalFilename).toLowerCase();
            if (!ALLOWED_FILE_TYPES.contains(extension)) {
                throw new IllegalArgumentException("不支持的文件类型：" + extension);
            }

            // 创建公告专属的上传目录
            Path announcementDir = Paths.get(uploadPath, "announcements", announcementId.toString());
            if (!Files.exists(announcementDir)) {
                Files.createDirectories(announcementDir);
            }

            // 生成唯一文件名
            String uniqueFilename = UUID.randomUUID().toString() + "." + extension;
            Path filePath = announcementDir.resolve(uniqueFilename);

            // 保存文件
            Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
            logger.info("文件已保存: {}", filePath);

            // 创建文件记录
            AnnouncementFile announcementFile = new AnnouncementFile();
            announcementFile.setAnnouncementId(announcementId);
            announcementFile.setFilename(originalFilename);
            announcementFile.setUrl("/uploads/announcements/" + announcementId + "/" + uniqueFilename);
            announcementFile.setSize(file.getSize());
            announcementFile.setType(extension);
            announcementFile.setUploadTime(LocalDateTime.now());
            announcementFile.setUploadedBy(userService.getCurrentUserId());

            // 保存到数据库
            fileMapper.insert(announcementFile);
            logger.info("文件记录已保存到数据库: {}", announcementFile);

            return announcementFile;

        } catch (Exception e) {
            logger.error("文件上传失败: {}", file.getOriginalFilename(), e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }
}